import { makeAutoObservable, observable, runInAction } from "mobx";
import type {
    IWorkflowStatesWidgetRepository,
    IWorkflowStatesWidgetRepositoryApproveStateStepParams,
    IWorkflowStatesWidgetRepositoryListResult,
    IWorkflowStatesWidgetRepositoryListStates,
    IWorkflowStatesWidgetRepositoryRejectStateStepParams,
    IWorkflowStatesWidgetRepositoryStartStateStepParams,
    IWorkflowStatesWidgetRepositoryTakeOverStateStepParams
} from "./abstractions/WorkflowStatesWidgetRepository.js";
import type { IWorkflowStatesWidgetGateway } from "~/Gateways/index.js";
import type { IGenericError, IWorkflowState } from "~/types.js";

export interface IWorkflowStatesWidgetRepositoryParams {
    gateway: IWorkflowStatesWidgetGateway;
}

interface ILoading {
    [key: string]: boolean;
}

interface IError {
    [key: string]: IGenericError | null;
}

export class WorkflowStatesWidgetRepository implements IWorkflowStatesWidgetRepository {
    private readonly _loading;
    private readonly _error;
    private _actionLoading: boolean = false;
    private _actionError: IGenericError | null = null;
    private readonly gateway;

    public get actionLoading(): boolean {
        return this._actionLoading;
    }

    public get actionError(): IGenericError | null {
        return this._actionError;
    }

    public get loading(): boolean {
        for (const key in this._loading) {
            if (this._loading[key]) {
                return true;
            }
        }
        return false;
    }

    public get error(): IGenericError | null {
        const errors = Object.keys(this._error).filter(key => !!this._error[key]);
        if (errors.length === 0) {
            return null;
        }
        return {
            message: "One or more errors occurred.",
            code: "MULTIPLE_ERRORS",
            data: this._error
        };
    }

    public constructor(params: IWorkflowStatesWidgetRepositoryParams) {
        this.gateway = params.gateway;
        this._error = observable.object<IError>({});
        this._loading = observable.object<ILoading>({});

        makeAutoObservable(this);
    }

    public async listOwnStates(
        params: IWorkflowStatesWidgetRepositoryListStates
    ): Promise<IWorkflowStatesWidgetRepositoryListResult> {
        const { state, limit } = params;
        const key = `own.${state}`;
        runInAction(() => {
            this._loading[key] = true;
            this._error[key] = null;
        });
        const result = await this.gateway.listOwnStates({
            where: {
                state
            },
            limit
        });
        runInAction(() => {
            this._loading[key] = false;
            this._error[key] = result.error;
        });
        return {
            items: result.data || [],
            totalCount: result.meta?.totalCount || 0
        };
    }

    public async listRequestedStates(
        params: IWorkflowStatesWidgetRepositoryListStates
    ): Promise<IWorkflowStatesWidgetRepositoryListResult> {
        const { state, limit } = params;
        const key = `requested.${state}`;
        runInAction(() => {
            this._loading[key] = true;
            this._error[key] = null;
        });
        const result = await this.gateway.listRequestedStates({
            where: {
                state
            },
            limit
        });
        runInAction(() => {
            this._loading[key] = false;
            this._error[key] = result.error;
        });
        return {
            items: result.data || [],
            totalCount: result.meta?.totalCount || 0
        };
    }

    public async startStateStep(
        params: IWorkflowStatesWidgetRepositoryStartStateStepParams
    ): Promise<IWorkflowState | null> {
        const key = `start.${params.id}`;
        runInAction(() => {
            this._loading[key] = true;
            this._error[key] = null;
        });
        const result = await this.gateway.startStateStep(params);
        runInAction(() => {
            this._loading[key] = false;
            this._error[key] = result.error;
        });
        return result.data || null;
    }

    public async takeOverStateStep(
        params: IWorkflowStatesWidgetRepositoryTakeOverStateStepParams
    ): Promise<IWorkflowState | null> {
        runInAction(() => {
            this._actionLoading = true;
            this._actionError = null;
        });
        const result = await this.gateway.takeOverStateStep(params);
        runInAction(() => {
            this._actionLoading = false;
            this._actionError = result.error;
        });
        return result.data || null;
    }

    public async approveStateStep(
        params: IWorkflowStatesWidgetRepositoryApproveStateStepParams
    ): Promise<IWorkflowState | null> {
        runInAction(() => {
            this._actionLoading = true;
            this._actionError = null;
        });
        const result = await this.gateway.approveStateStep(params);
        runInAction(() => {
            this._actionLoading = false;
            this._actionError = result.error;
        });
        return result.data || null;
    }

    public async rejectStateStep(
        params: IWorkflowStatesWidgetRepositoryRejectStateStepParams
    ): Promise<IWorkflowState | null> {
        runInAction(() => {
            this._actionLoading = true;
            this._actionError = null;
        });
        const result = await this.gateway.rejectStateStep(params);
        runInAction(() => {
            this._actionLoading = false;
            this._actionError = result.error;
        });
        return result.data || null;
    }
}
